package net.entelijan.cobean.bind.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import net.entelijan.cobean.bind.IModelChangeListener;

import org.aopalliance.aop.Advice;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Advisor;
import org.springframework.beans.BeanWrapperImpl;

final class CobeanSpringUtils {

    private static Log log = LogFactory.getLog(CobeanSpringUtils.class);
    
    private CobeanSpringUtils() {
        super();
    }

    public static Class<?> getOriginalClassFromDynamicProxy(Class<?> wrappedClass) {
        throw new IllegalStateException("Not yet impl. class='" + wrappedClass.getName()
                + "' interfaces='" + Arrays.toString(wrappedClass.getInterfaces()) + "'");
    }

    public static Object getPlaceholderFromDynamicProxy(Class<?> wrappedClass) {
        return Proxy.newProxyInstance(CobeanSpringUtils.class.getClassLoader(), wrappedClass
                .getInterfaces(), new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                log.debug("[invoke] " + method.getName());
                return null;
            }
        });
    }

    public static Class<?> getOriginalClassFromCglib(Class<?> cglibClass) {
        return cglibClass.getSuperclass();
    }

    public static <T> List<IModelChangeListener> getModelChangeListener(T model) {
        List<IModelChangeListener> modelChangeListeners;
        if (CobeanSpringUtil.isCglibProxy(model)) {
            modelChangeListeners = getModelChangeListenersFromCglibProxy(model);
        } else if (CobeanSpringUtil.isDynamicProxy(model)) {
            modelChangeListeners = getModelChangeListenersFromDynamicProxy(model);
        } else {
            throw new IllegalStateException("Cannot get a model change listener if the model '"
                    + model + "' of class '" + model.getClass() + "' is not wrapped");
        }
        return modelChangeListeners;
    }

    private static List<IModelChangeListener> getModelChangeListenersFromDynamicProxy(Object proxy) {
        List<IModelChangeListener> re = new ArrayList<IModelChangeListener>();
        BeanWrapperImpl w = new BeanWrapperImpl(proxy);
        if (w.isReadableProperty("advisors")) {
            Advisor[] advisors = (Advisor[]) w.getPropertyValue("advisors");
            if (advisors != null) {
                IModelChangeListenersAware mcla = null;
                for (Advisor advisor : advisors) {
                    Advice advice = advisor.getAdvice();
                    if (advice instanceof IModelChangeListenersAware) {
                        if (mcla != null) {
                            throw new IllegalStateException(
                                    "There is more than one IModelChangeListenersAware advice connected connected to bean: '"
                                            + proxy + "'");
                        }
                        mcla = (IModelChangeListenersAware) advice;
                    }
                }
                if (mcla != null) {
                    re = mcla.getModelChangeListeners();
                }
            }
        }
        return re;
    }

    private static List<IModelChangeListener> getModelChangeListenersFromCglibProxy(Object proxy) {
        List<IModelChangeListener> re = null;
        BeanWrapperImpl wrapper = new BeanWrapperImpl(proxy);
        String advisorsPropName = "advisors";
        if (wrapper.isReadableProperty(advisorsPropName)) {
            Advisor[] advisors = (Advisor[]) wrapper.getPropertyValue(advisorsPropName);
            for (Advisor adv : advisors) {
                if (adv.getAdvice() instanceof IModelChangeListenersAware) {
                    IModelChangeListenersAware advice = (IModelChangeListenersAware) advisors[0]
                            .getAdvice();
                    if (re != null) {
                        throw new IllegalStateException(
                                "There is more than one IModelChangeListenersAware advice connected connected to bean: '"
                                        + proxy + "'");
                    }
                    re = advice.getModelChangeListeners();
                }
            }

        }
        return re;
    }

}
